//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//
//
//
// PMUIISR.c 
//
// Generic Installable Interrupt Service Routine.
//
// this source file is modified from GIISR.c
//
#include <windows.h>
#include <nkintr.h>
#include <ceddk.h>
#include <giisr.h>

//------------------------------------------
#include <prm_registration.h>
#include <oal.h>        //OALMSG

extern PRMStoreInfo *ginfo;
extern int ActiveClientIDPMU;
HANDLE hthrd_wISR=NULL;




//Function
extern BOOL GetAcitiveclientsIDs();
extern PRMClient *PRMGetClientbyID();
int PRMGetISRGroupIndex(PRMPriority prio);
BOOL  FreeIntChainThread(ISRFrEvents *events);



//----------------------------------------------------------------------------
//clients can call this function to free its own orginal ISR when it wants to change a ISR dll.
//
//If clients want to do unregistration of ISR, just call PMUUnRegisterISR
//also, freeing ISR will be implicitly done when clients do a freeing resource commit.
//
//In this function:
//Do not free the events here. the events are using the same names. we can keep them.
//"events leakage" will not happen.
//we'll do events freeing in a free commit. not here
//
//For a low priority client, we do "Thread exit" here.
//June 17, 2005
//DataBase Update.
//

BOOL WINAPI PMUUnRegisterISR(PRMHandle hclient, HANDLE pmu_IsrHandler)
{	
	DWORD exitcode;
	PRMClient *client;
	int gindex=0xFF;
	int id[2];

	OALMSG(PRMDEBUG_L, (L"+PMUUnRegisterISR\r\n"));
	
	client=PRMGetClientbyID(hclient.ID);

	GetAcitiveclientsIDs(id);
	ActiveClientIDPMU=id[0];
	if (!(hclient.ID!=0 && ActiveClientIDPMU==hclient.ID))//judge the access
	{
		OALMSG(PRMDEBUG,(TEXT("[PMUUnRegisterISR:]hclient.ID!=0 && ActiveClientIDPMU==hclient.ID\r\n")));		
		return FALSE;
	}
	if (pmu_IsrHandler == NULL)
	{
		OALMSG(PRMDEBUG,(TEXT("[PMUUnRegisterISR:]pmu_IsrHandler == NULL\r\n")));	
		return FALSE;
	}
	if (TRUE!=FreeIntChainHandler(pmu_IsrHandler))
	{
		OALMSG(PRMDEBUG,(TEXT("PMUUnRegisterISR:FreeIntChainHandler failed 0x%x\r\n"),pmu_IsrHandler));
		return FALSE;
	}
	
	//Thread Freeing
	if (hthrd_wISR!=NULL && client->priority==PRM_LOW)
	{

		GetExitCodeThread(hthrd_wISR, &exitcode);
		//ExitThread(exitcode);
		if (0!=TerminateThread(hthrd_wISR, exitcode))
			OALMSG(PRMDEBUG,(TEXT("exit Thread\r\n")));	
		
		hthrd_wISR=NULL;
	}	

	
	//Update DataBase
	//Save handler to the database
	if (PRM_LOW== client->priority)
	{
		gindex=PRMGetISRGroupIndex(PRM_LOW);
		//Assert (gindex != 0xFF)
		if (gindex==0xFF)
		{
			OALMSG(PRMDEBUG, (TEXT("PRMGetISRGroupIndex fail\r\n")));
			return FALSE;
		}	
		client->Group[gindex]->pmuISRHander=NULL;
	}
	else if(PRM_HIGH==client->priority)
	{
		gindex=PRMGetISRGroupIndex(PRM_HIGH);
		//Assert(gindex !=0xFF)
		if (gindex==0xFF)
		{
			OALMSG(PRMDEBUG, (TEXT("PRMGetISRGroupIndex fail\r\n")));
			return FALSE;
		}
		client->Group[gindex]->pmuISRHander=NULL;
	}

//	CloseHandle(ISREvent);
//	ginfo->prmResource[PMUISR].prmisr.ISREvent=NULL;
	OALMSG(PRMDEBUG, (TEXT("-PRM ISRUnregister success\r\n")));
	return TRUE;
}


//----------------------------------------------------------------------------
//PMUForceUnInstallIISR
//
BOOL WINAPI PMUForceUnInstallIISR(HANDLE pmu_IsrHandler)
{
	BOOL ret;
	if (pmu_IsrHandler == NULL)
		return FALSE;
	ret=FreeIntChainHandler(pmu_IsrHandler);
	
	if (ret==TRUE)
	{
		OALMSG(PRMDEBUG, (TEXT("[PMUForceUnInstallISR]:freeintchainHandler success 0x%x\r\n"), pmu_IsrHandler));
	}
	else
	{
		OALMSG(PRMDEBUG, (TEXT("[PMUForceUnInstallISR]:freeintchainHandler fail 0x%x\r\n"), pmu_IsrHandler));
	}

	return ret;
}

//------------------------------------------------------------------------
//PMURegisterISR
//clients use this API to register the ISR
//parameters:
//hclient: this handle is given by PRMOpenSession
//pmu_IsrHandler:  the IsrHandler obtained by loadintchainhandler
//ISRdllName: clients need to provide the name of the DLL which implement the ISRHandler function
//                  refer to giir for the implementation of this DLL.
//pmuirq: should be 12
//
//If clients want to do unregistration of ISR, just call PMUUnRegisterISR
//also, freeing ISR will be implicitly done when clients do a freeing resource commit.
//
//June 17, 2005
////	if (!pmu_IsrHandler) 
// -->	if ((*pmu_IsrHandler)==NULL)

BOOL WINAPI PMURegisterISR(PRMHandle hclient, HANDLE *pmu_IsrHandler,
	LPCWSTR ISRdllName, LPCWSTR ISRHandlerName, BYTE pmuirq)
{
	PRMClient *client=NULL;
	int i=0; 
	int cleanflag=0x0;
	ISRFrEvents events;
	int id[2];


	int gindex=0xFF;
	client=PRMGetClientbyID(hclient.ID);

	if (client==NULL)
		return FALSE;

	GetAcitiveclientsIDs(id);
	ActiveClientIDPMU=id[0];

	if (ActiveClientIDPMU!=hclient.ID)//judge the access
	{
		OALMSG(PRMDEBUG, (TEXT("PMURegisterISR:No access to ISR\r\n")));
		return FALSE;
	}
	if (pmu_IsrHandler==NULL)
	{
		OALMSG(PRMDEBUG,(TEXT("user provided pmu_IsrHandler variable is NULL\r\n")));
		return FALSE;
	}
	
	*pmu_IsrHandler = LoadIntChainHandler(ISRdllName, ISRHandlerName, pmuirq);

//	if (!pmu_IsrHandler) 
	if ((*pmu_IsrHandler)==NULL)
	{
		OALMSG(PRMDEBUG, (_T(
		"PRM LoadIntChainHandler failed\r\n"
		)));
		return FALSE;
	}

	OALMSG(PRMDEBUG, (_T("PMURegisterISR:LoadIntChainHandler successfully 0x%x\r\n"),*pmu_IsrHandler));

	//Save handler to the database
	if (PRM_LOW== client->priority)
	{
		OALMSG(PRMDEBUG,(TEXT("Save Low priority ISRHandler to database\r\n")));
		gindex=PRMGetISRGroupIndex(PRM_LOW);
		//Assert (gindex != 0xFF)
		if (gindex==0xFF)
		{
			OALMSG(PRMDEBUG, (TEXT("PRMGetISRGroupIndex fail\r\n")));
			return FALSE;
		}	
		
		client->Group[gindex]->pmuISRHander=*pmu_IsrHandler;
	}
	else if(PRM_HIGH==client->priority)
	{
		OALMSG(PRMDEBUG,(TEXT("Save High priority ISRHandler to database\r\n")));
		gindex=PRMGetISRGroupIndex(PRM_HIGH);
		//Assert(gindex !=0xFF)
		if (gindex==0xFF)
		{
			OALMSG(PRMDEBUG, (TEXT("PRMGetISRGroupIndex fail\r\n")));
			return FALSE;
		}
		client->Group[gindex]->pmuISRHander=*pmu_IsrHandler;
	}
	

//Create Two event objects
//ptISRevent: indicate the owner of the ISR process to free ISR
//frISRevent:  tell other processes I have done the free operation successfully

	events.ptISRevent=CreateEvent (NULL,  // No security attributes
                       FALSE,  // FALSE:Manual
                       FALSE,  // Initial state is non-signaled
                      TEXT("PTISREVNET"));

	if (events.ptISRevent==NULL)
	{
		goto CleanUp;
	}
	cleanflag=0x1;
	events.frISRevent=CreateEvent (NULL,  // No security attributes
                       FALSE,  // FALSE:Manual
                       FALSE,  // Initial state is non-signaled
                      TEXT("FRISREVENT"));
	if (events.frISRevent==NULL)
	{
		goto CleanUp;
	}
	cleanflag=0x2;

	ResetEvent(events.ptISRevent);
	ResetEvent(events.frISRevent);

//Save to database
// when HIGH, no need to create the event !!!
	if(client->priority==PRM_LOW)
	{
		gindex=PRMGetISRGroupIndex(PRM_LOW);
		client->Group[gindex]->events.ptISRevent=
			events.ptISRevent;
		client->Group[gindex]->events.frISRevent=
			events.frISRevent;
	}


//GetLastError returns ERROR_ALREADY_EXISTS
//Create the thread to wait for "free ISR" signal.
//    

      if (client->priority==PRM_LOW)
      	{
      //	OALMSG(PRMDEBUG, (TEXT("create : events->ptISRevent is 0x%x\r\n"), events.ptISRevent));
	//OALMSG(PRMDEBUG, (TEXT("create : events->frISRevent is 0x%x\r\n"), events.frISRevent));	
		hthrd_wISR = CreateThread(NULL,
						0,
						(LPTHREAD_START_ROUTINE)FreeIntChainThread,
						&client->Group[gindex]->events,
						0,
						NULL);
		if (hthrd_wISR==NULL)
		goto CleanUp;
      	}


	OALMSG(PRMDEBUG, (TEXT("-PMURegisterISR success\r\n")));
	return TRUE;
CleanUp:
	OALMSG(PRMDEBUG, (TEXT("CleanUp:\r\n")));
	if (TRUE!=FreeIntChainHandler(*pmu_IsrHandler))
	{
		OALMSG(PRMDEBUG,(TEXT("FreeIntChainHandler Failed\r\n")));
	}
	switch (cleanflag)
	{
		case 0x1:
			CloseHandle(events.ptISRevent);
			break;
		case 0x2:
			CloseHandle(events.ptISRevent);
			CloseHandle(events.frISRevent);
			break;
	}
	return FALSE;
}


//---------------------------------------------------------------------
//This Thread is established to wait for FreeIntChainHandler that it previously 
//registered. because there is no way to force the "FreeIntChainHandler" in 
//another process or in the kernel. So we use event object to inform the client
//who registered the ISR to free this ISR itself. Only after freeing this resource,
//the other client can start the using of PMU. this will be garanteed in PRMCommit.
//
//Parameter:
//events:  two event to synchronize the Freeing process of Installable ISR
//return value:
//TRUE when sucess. FALSE when failure
//
BOOL  FreeIntChainThread(ISRFrEvents *events)
{

	int gindex;
	DWORD dwWaitResult;
	PRMClient *client;
	int id[2];
	OALMSG(PRMDEBUG_L, (TEXT("free Threading started.......\r\n")));
	OALMSG(PRMDEBUG_L, (TEXT("events->ptISRevent is 0x%x\r\n"), events->ptISRevent));
	OALMSG(PRMDEBUG_L, (TEXT("events->frISRevent is 0x%x\r\n"), events->frISRevent));
	
    	dwWaitResult = WaitForSingleObject (events->ptISRevent, INFINITE);

	OALMSG(PRMDEBUG_L, (TEXT("pass wait ptISRevent\r\n")));

	GetAcitiveclientsIDs(id);
	ActiveClientIDPMU=id[0];
	//Get ActiveClient
	//The ActiveClientIDPMU must be a low client
	client=PRMGetClientbyID(ActiveClientIDPMU);
	
	// Only a low priority client has to create this thread.

	gindex=PRMGetISRGroupIndex(PRM_LOW);
	if (gindex==0xFF)
	{
		OALMSG(PRMDEBUG,(TEXT("PRMGetISRGroupIndex failed\r\n")));
		return FALSE;
	}	
 
	if (client->Group[gindex]->pmuISRHander!=NULL)
	{
		if (PMUForceUnInstallIISR(client->Group[gindex]->pmuISRHander))
		{
			client->Group[gindex]->pmuISRHander=NULL;
		}	
		else
		{
			OALMSG(PRMDEBUG,(TEXT("FreeIntChainThread:free failed\r\n")));
		}
		
	}

	SetEvent(events->frISRevent);

	return TRUE;
	//when returned, this thread is implictly exit. cause ExitThread is implicitly called.
	
}

//---------------------------------------------------------------------------
//Generally users take PMU resources as Group 0, the first group
//but some user may prefer to apply PMU resources to Group 1
//The ISR resource belongs to the same group of PMU resources.
//
//This function actually returns the Group index of PMU resources.
//
//parameter 
//prio:  Indicate which active client's group index is needed. the  low priority client's or the
//high priority's.
//return value:
//0xFF:  no such group
//0~1 : Group Index
//
int PRMGetISRGroupIndex(PRMPriority prio)
{
	UINT32 activeres=0xFF;
	UINT32  i;
	//Decide which group belong to.	
	for (i=0;i<5;i++)
	{
		if (ginfo->prmResource[i].Access.ID!=0)  //PRM_BLOCK is not used
		{
			activeres=i;
			break;
		}
	}

	if (activeres!=0xFF)
		return prio==PRM_HIGH ? ginfo->prmResource[activeres].Hi.GroupIndex
		:ginfo->prmResource[activeres].Lo.GroupIndex;
	else 
		return 0xFF;
	
}


